#include "ProcessBar.h"
#include <string.h>

#define SIZE 101                 // 定义进度条的大小为 101（用于表示 100% 的进度和一个终止字符）
#define MAX_RATR 100             // 最大的进度比例，设置为 100
#define STYLE '*'                // 进度条的显示字符，这里选择的是 #
#define STIME 1000 * 200         // 每次更新进度条的时间延迟，设置为 200 毫秒（1000 微秒 × 200）
#define BODY '='                 // 进度条身体
#define TAIL '>'                 // 进度条尾巴
typedef void (*callback_t)(int); // 函数指针类型

char processbar[SIZE];
// 定义一个字符串，包含四个字符，用于在进度条更新时显示不同的进度指示符
const char *str = "|/-\\";

void ProcessBar1()
{
    // version1
    int rate = 0;                   // 初始化进度变量 rate 为 0。
    char Bar[SIZE];                 // 创建一个字符数组 Bar 用于表示进度条
    memset(Bar, '\0', sizeof(Bar)); // 将 Bar 数组的所有元素初始化为 '\0'，即清空数组。
    int num = strlen(str);          // 计算字符串 str 的长度（4），用于后续的指示符更新
    while (rate <= MAX_RATR)        // 当进度小于等于 100 时，继续循环。
    {
        // 打印格式化的字符串
        //%-100s: 打印进度条，宽度为 100，左对齐
        //%d%%: 打印当前进度百分比  %%: 是用来打印字面上的百分号字符 %
        //%c: 打印当前进度指示符
        //\r: 回车符，使下一次输出覆盖当前行
        printf("[%-100s][%d%%][%c]\r", Bar, rate, str[rate % num]);
        // 刷新输出缓冲区，确保进度条立即显示。
        fflush(stdout);
        // 暂停程序执行一段时间（200 毫秒），用于控制更新速度。
        usleep(STIME);
        // 将当前进度位置用 # 更新，rate++ 后自增
        Bar[rate++] = STYLE;
    }
    printf("\n");
}
void ProcessBar2()
{
    // version2
    int rate = 0;                   // 初始化进度变量 rate 为 0。
    char Bar[SIZE];                 // 创建一个字符数组 Bar 用于表示进度条
    memset(Bar, '\0', sizeof(Bar)); // 将 Bar 数组的所有元素初始化为 '\0'，即清空数组。
    int num = strlen(str);          // 计算字符串 str 的长度（4），用于后续的指示符更新
    while (rate <= MAX_RATR)        // 当进度小于等于 100 时，继续循环。
    {
        //\033[: 开始一个 ANSI 转义序列。\033 是 ASCII 字符 27（ESC），[ 后面跟随的数字定义了颜色和格式。
        //%d;%d: 这里有两个 %d，它们用于随机生成背景和前景的颜色。
        // 第一个 %d：rand() % 10 + 40，生成一个范围在 40 到 49 之间的整数，用于背景色（40-49 是标准的 ANSI 颜色代码）。
        // 第二个 %d：rand() % 10 + 30，生成一个范围在 30 到 39 之间的整数，用于前景色（30-39 是标准的 ANSI 颜色代码）。
        //%dm 是 printf 函数中用于格式化字符串的一个占位符，通常用于指定 ANSI 转义码中的颜色或样式。
        // 在这个中，d代表一个整数，m用于结束颜色或样式的设置。
        //\033[0m: 重置所有的属性和颜色，将后续的输出恢复到默认状态。这确保了前面的颜色设置只影响了进度条的显示，而不会影响之后的输出。
        //\033[40;%dm[%d%%]:(显示进度百分比)
        // rand() % 10 + 30：用于设置前景色，显示当前进度百分比。
        //[%d%%]：显示当前的进度百分比，rate 是当前进度值。

        //\033[40;%dm[%c]:(显示进度指示符)
        // rand() % 10 + 30：又一次设置前景色，用于显示当前的进度指示符。
        //[%c]：显示当前的进度指示符，使用 str[rate % num] 来从 str 字符串中选择一个字符。
        printf("\033[%d;%dm[%-100s]\033[0m\033[40;%dm[%d%%]\033[0m\033[40;%dm[%c]\033[0m\r",
               rand() % 10 + 40, rand() % 10 + 30, Bar, rand() % 10 + 30, rate, rand() % 10 + 30, str[rate % num]);
        // 刷新输出缓冲区，确保进度条立即显示。
        fflush(stdout);
        // 暂停程序执行一段时间（200 毫秒），用于控制更新速度。
        usleep(STIME);
        // 将当前进度位置用 # 更新，rate++ 后自增
        Bar[rate++] = STYLE;
    }
    printf("\n");
}

void ProcessBar3()
{
    // version3
    int rate = 0;                   // 初始化进度变量 rate 为 0。
    char Bar[SIZE];                 // 创建一个字符数组 Bar 用于表示进度条
    memset(Bar, '\0', sizeof(Bar)); // 将 Bar 数组的所有元素初始化为 '\0'，即清空数组。
    int num = strlen(str);          // 计算字符串 str 的长度（4），用于后续的指示符更新
    while (rate <= MAX_RATR)        // 当进度小于等于 100 时，继续循环。
    {
        // 打印格式化的字符串
        //%-100s: 打印进度条，宽度为 100，左对齐
        //%d%%: 打印当前进度百分比  %%: 是用来打印字面上的百分号字符 %
        //%c: 打印当前进度指示符
        //\r: 回车符，使下一次输出覆盖当前行
        printf("[%-100s][%d%%][%c]\r", Bar, rate, str[rate % num]);
        // 刷新输出缓冲区，确保进度条立即显示。
        fflush(stdout);
        // 暂停程序执行一段时间（200 毫秒），用于控制更新速度。
        usleep(STIME);
        // 将当前进度位置用 # 更新，rate++ 后自增
        Bar[rate++] = BODY;
        // 此时我们的rate在下一个位置，我们让=的下一个位置弄为>
        // 小心越界问题
        if (rate <= 100)
            Bar[rate] = TAIL;
    }
    printf("\n");
}

void ProcessBar4(int rate)
{
    // 版本4
    // 我们重点理解一下我们的进度条是如何被调用的
    // 判断越界
    if (rate < 0 || rate > 100)
        return;
    int num = strlen(str);
    printf("[%-100s][%d%%][%c]\r", processbar, rate, str[rate % num]);
    fflush(stdout);
    processbar[rate++] = BODY;
    if (rate < 100)
        processbar[rate] = TAIL;
}

// 模拟一种安装或者下载
void DownLoad(callback_t cb)
{
    int total = 1000; // 假设我们有一个1000MB的东西
    int curr = 0;     // 当前下载了多少MB;
    while (curr <= total)
    {
        // 执行着某种下载的任务
        usleep(50000);
        int rate = curr * 100 / total;

        cb(rate); // 通过回调，展示进度。

        curr += 10; // 每次下载10MB
    }
    printf("\n");
}

void InitBar()
{
    memset(processbar, '\0', sizeof(processbar));
}

// 倒计时
void Countdown()
{
    int Size = 10;
    while (Size)
    {
        printf("%-2d\r", Size);
        fflush(stdout);
        sleep(1);
        Size--;
    }
    // printf("hello world");
}
